﻿using Forum.Cache;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace Forum.Filter
{
    public class PreventDuplicateRequestsActionFilter : IAsyncActionFilter
    {
        public string[] FactorNames { get; set; }
        public int? Seconds { get; set; }

        public PreventDuplicateRequestsActionFilter()
        {
        }

        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // 定义一个 string? 类型数组 factorValues，并初始化为 null
            var factorValues = new string?[FactorNames.Length];
            // 判断请求参数是否来自于请求体
            var isFromBody = context.ActionDescriptor.Parameters.Any(r => r.BindingInfo?.BindingSource == BindingSource.Body);
            // 如果请求参数来自于请求体
            if (isFromBody)
            {
                // 从请求参数中获取值，并根据 FactorNames 的值来构造 factorValues 数组
                var parameterValue = context.ActionArguments.FirstOrDefault().Value;
                factorValues = FactorNames.Select(name => parameterValue?.GetType().GetProperty(name)?.GetValue(parameterValue)?.ToString()).ToArray();
            }
            // 如果请求参数不来自于请求体
            else
            {
                // 从控制器方法参数中获取值，并根据 FactorNames 的值来构造 factorValues 数组
                for (var index = 0; index < FactorNames.Length; index++)
                {
                    if (context.ActionArguments.TryGetValue(FactorNames[index], out var factorValue))
                    {
                        factorValues[index] = factorValue?.ToString();
                    }
                }
            }

            // 如果 factorValues 中所有元素都为 null 或空字符串，则直接调用下一个中间件
            if (factorValues.All(string.IsNullOrEmpty))
            {
                await next();
                return;
            }

            // 构造幂等性key，并从缓存中获取对应的值
            var idempotentKey = $"{context.HttpContext.Request.Path.Value}:{string.Join("-", factorValues)}";
            var idempotentValue = CacheFactory.Cache.GetCache<string>(idempotentKey);
            // 如果幂等性key对应的值不为 null，则返回一个自定义的 JsonResult 对象
            if (idempotentValue != null)
            {
                context.Result = new JsonResult(new { code = StatusCodes.Status200OK, msg = "正在调用接口，请勿重复调用！", data = "" });
            }
            else
            {
                // 如果幂等性key对应的值为 null，则将幂等性key和当前时间作为缓存值存入缓存，并调用下一个中间件
                CacheFactory.Cache.SetCache<string>(idempotentKey, DateTimeOffset.UtcNow.ToString(), DateTime.Now.AddSeconds(Convert.ToDouble(Seconds)));
                await next();
            }
        }
    }
}
